<template>
  <div class="grid">
    <div
      v-for="(row, r_index) in grid"
      :key="r_index"
      class="row"
    >
      <div
        v-for="(col, c_index) in row"
        :key="c_index"
        class="col"
        :class="`dot-${col}`"
      ></div>
    </div>
  </div>
</template>

<script lang="ts">
// import { Options, Vue } from 'vue-class-component'
import { ref, defineComponent, onMounted, onUnmounted } from 'vue'

interface SPosition {
  x: number;
  y: number;
}

interface DownKeyMap {
  [ArrowLeft: string]: number;
  ArrowRight: number;
  ArrowUp: number;
  ArrowDown: number;
}

const directionMap: DownKeyMap = {
  ArrowLeft: 0,
  ArrowRight: 1,
  ArrowUp: 2,
  ArrowDown: 3
}

const roomMap = {
  wall: 9,
  foot: 5,
  snake: 1,
  empty: 0
}

export default defineComponent({
  name: 'tcs',
  setup () {
    const height = 20
    const width = 30
    const grid = ref<Array<Array<number>>>([])
    const speed = 500
    let interval: any = null
    let isEatFood = false
    let direction = directionMap.ArrowRight
    let directionFlag = true // 每次移动只能改变一次方向
    const snake: Array<SPosition> = [
      { x: 4, y: 2 },
      { x: 3, y: 2 },
      { x: 2, y: 2 }
    ]

    function generateGrid () {
      for (let h = 0; h < height; h++) {
        for (let w = 0; w < width; w++) {
          if (!grid.value[h]) {
            grid.value[h] = []
          }
          if (
            h === 0 ||
            (height - 1 === h) ||
            w === 0 ||
            (width - 1 === w)
          ) {
            grid.value[h][w] = roomMap.wall
          } else {
            grid.value[h][w] = roomMap.empty
          }
        }
      }
    }

    function generateSnake (tail?: SPosition) {
      for (let n = 0; n < snake.length; n++) {
        grid.value[snake[n].y][snake[n].x] = 1
      }
      if (tail && (tail.x !== snake[0].x || tail.y !== snake[0].y)) {
        console.info(tail, snake[0], tail.x === snake[0].x && tail.y === snake[0].y)
        grid.value[tail.y][tail.x] = 0
      }
    }

    function checkRoom () {
      const arr = []
      for (let h = 0; h < height; h++) {
        for (let w = 0; w < width; w++) {
          if (!grid.value[h][w]) {
            arr.push([h, w])
          }
        }
      }
      return arr
    }

    function randomGenerateFood () {
      const arr = checkRoom()
      const food = arr[Math.floor(Math.random() * arr.length)]
      grid.value[food[0]][food[1]] = roomMap.foot
    }

    function addSnake (position: SPosition) {
      isEatFood = false
      snake.unshift({
        x: position.x,
        y: position.y
      })
      generateSnake()
      randomGenerateFood()
      // clearInterval(interval)
    }

    function checkAvailable (positon: SPosition) {
      return grid.value[positon.y][positon.x]
    }

    function getVector (direction: number) {
      return {
        [directionMap.ArrowDown]: { x: 0, y: 1 },
        [directionMap.ArrowUp]: { x: 0, y: -1 },
        [directionMap.ArrowLeft]: { x: -1, y: 0 },
        [directionMap.ArrowRight]: { x: 1, y: 0 }
      }[direction]
    }

    function snakeMove (vector: SPosition) {
      let next: SPosition | null = null
      let last: SPosition | null = null
      for (let n = 0; n < snake.length; n++) {
        if (n === 0) {
          last = {
            x: snake[n].x,
            y: snake[n].y
          }
          snake[n].x = snake[n].x + vector.x
          snake[n].y = snake[n].y + vector.y
        } else {
          next = {
            x: (last as SPosition).x,
            y: (last as SPosition).y
          }
          last = snake[n]
          snake[n] = next as SPosition
        }
      }
      generateSnake(last as SPosition)
      directionFlag = true
    }

    function checkMove () {
      const vector = getVector(direction)
      const next = {
        x: snake[0].x + vector.x,
        y: snake[0].y + vector.y
      }
      const type = checkAvailable(next)
      const tail = snake[snake.length - 1]
      if (type === roomMap.foot) {
        isEatFood = true
      } else if (type === roomMap.wall || (type === roomMap.snake && (tail.x !== next.x || tail.y !== next.y))) {
        clearInterval(interval)
        console.info('游戏结束')
        return
      }
      if (isEatFood) {
        addSnake(next)
      } else {
        snakeMove(vector)
      }
    }

    function downKey (e: KeyboardEvent) {
      const vector = getVector(directionMap[e.key])
      const currentVector = getVector(direction)
      if (
        (Math.abs(vector.x) !== Math.abs(currentVector.x)) &&
        (Math.abs(vector.y) !== Math.abs(currentVector.y)) && directionFlag
      ) {
        directionFlag = false
        direction = directionMap[e.key]
      }
    }

    onMounted(() => {
      document.addEventListener('keydown', downKey)
      generateGrid()
      generateSnake()
      randomGenerateFood()
      interval = setInterval(checkMove, speed)
    })

    onUnmounted(() => {
      clearInterval(interval)
      document.removeEventListener('keydown', downKey)
    })
    return {
      grid
    }
  }
})

</script>

<style scoped lang="less">
.grid {
  display: inline-block;
  border: 1px solid #e0e0e0;
  .row {
    display: flex;
    .col {
      border: 1px solid #e0e0e0;
      width: 10px;
      height: 10px;
      &.dot-9 {
        background: #584d4d;
      }
      &.dot-5 {
        background: red;
      }
      &.dot-1 {
        background: darkcyan;
      }
    }
  }
}
</style>
